package org.dromara.common.mybatis.helper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.mybatis.enums.DataBaseType;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 数据库助手
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataBaseHelper {

    private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);

    /**
     * 获取当前数据库类型
     */
    public static DataBaseType getDataBaseType() {
        DataSource dataSource = DS.determineDataSource();
        try (Connection conn = dataSource.getConnection()) {
            DatabaseMetaData metaData = conn.getMetaData();
            String databaseProductName = metaData.getDatabaseProductName();
            return DataBaseType.find(databaseProductName);
        } catch (SQLException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    public static boolean isMySql() {
        return DataBaseType.MY_SQL == getDataBaseType();
    }

    public static boolean isOracle() {
        return DataBaseType.ORACLE == getDataBaseType();
    }

    public static boolean isPostgerSql() {
        return DataBaseType.POSTGRE_SQL == getDataBaseType();
    }

    public static boolean isSqlServer() {
        return DataBaseType.SQL_SERVER == getDataBaseType();
    }

    public static String findInSet(Object var1, String var2) {
        DataBaseType dataBasyType = getDataBaseType();
        String var = Convert.toStr(var1);
        if (dataBasyType == DataBaseType.SQL_SERVER) {
            // charindex(',100,' , ',0,100,101,') <> 0
            return "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2);
        } else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
            // (select position(',100,' in ',0,100,101,')) <> 0
            return "(select position(',%s,' in ','||%s||',')) <> 0".formatted(var, var2);
        } else if (dataBasyType == DataBaseType.ORACLE) {
            // instr(',0,100,101,' , ',100,') <> 0
            return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var);
        }
        // find_in_set(100 , '0,100,101')
        return "find_in_set('%s' , %s) <> 0".formatted(var, var2);
    }

    /**
     * 获取当前加载的数据库名
     */
    public static List<String> getDataSourceNameList() {
        return new ArrayList<>(DS.getDataSources().keySet());
    }

    /**
     * 填充时间范围查询的查询条件
     * DataBaseHelper.fillTimeRangeSelect(lqw, bo, AbnormalReport::getAbnormalTime);
     * @param lqw
     * @param query
     * @param column
     * @param <P>
     * @param <T>
     */
    public static <P, T extends BaseEntity> void fillTimeRangeSelect(LambdaQueryWrapper<P> lqw, T query, SFunction<P, ?> column) {
        Map<String, Object> params = ObjUtil.defaultIfNull(query.getParams(), MapUtil.empty());
        // 获取开始时间的字段名，与结束时间的字段名
        String fieldName = getFileName(column);
        String beginFieldName = StrUtil.format("{}Begin", fieldName);
        String endFieldName = StrUtil.format("{}End", fieldName);

        // 要求开始时间与结束时间都不为空
        Object beginVal = params.get(beginFieldName);
        Object endVal = params.get(endFieldName);
        if (!ObjUtil.isAllNotEmpty(beginVal, endVal)) {
            return;
        }

        // 设置查询条件
        DateTime beginTime = DateUtil.beginOfDay(DateUtil.parseDate(beginVal.toString()));
        DateTime dateTime = DateUtil.endOfDay(DateUtil.parseDate(endVal.toString()));
        lqw.between(column, beginTime, dateTime);
    }

    private static <T> String getFileName(SFunction<T, ?> method) {
        LambdaMeta meta = LambdaUtils.extract(method);
        return BeanUtil.getFieldName(meta.getImplMethodName());
    }
}
